# -* coding= utf-8 -*-
import pandas as pd
import QUANTAXIS as QA
from StrategyParameter import CrossSignal, OverSignal, AdviseX, CrossX, Advise, Common


class Indicators:
    running_time = None
    macd = None
    macd_cross = CrossSignal()
    # RSI KDJ 同时穿才算 这样可以避免一些小的穿越波动
    rsi = None
    rsi_cross = CrossSignal()
    rsi_over = OverSignal()
    kdj = None
    kdj_cross = CrossSignal()
    kdj_over = OverSignal()
    ma_distance = None
    # 平仓信号提前发生
    cci = None
    cci_over = OverSignal()
    advise = None

    bar = None

    def analyze(self, bar, market_data, begin_trade_time):
        self.bar = bar
        last_over_points = None
        if self.advise is not None:
            last_over_points = self.advise.over_points
        self.advise = Advise()
        if last_over_points is not None:
            self.advise.last_over_points = last_over_points

        self.running_time = bar.running_time
        market_data.index.names = ["datetime", "code"]

        data = market_data[-60:]

        self.macd = QA.QA_indicator_MACD(data)
        self.macd = self.macd.dropna()

        self.rsi = QA.QA_indicator_RSI(data, 6, 12, 24)
        self.rsi[self.rsi == 0] = None
        self.rsi = self.rsi.dropna()
        self.rsi[["a", "b", "c", "d"]] = [80, 20, 90, 10]

        self.kdj = QA.QA_indicator_KDJ(data)
        self.kdj[self.kdj == 12.5000] = None
        self.kdj = self.kdj.dropna()
        self.kdj[["a", "b", "c", "d"]] = [100, 10, 110, 0]

        self.cci = QA.QA_indicator_CCI(data)
        self.cci = self.cci.dropna()
        self.cci[["c", "d"]] = [200, -200]

        self.macd_cross = self.get_cross(self.macd.DIF, self.macd.DEA, self.macd.DEA)
        self.rsi_cross = self.get_cross(self.rsi.RSI1, self.rsi.RSI2)
        self.kdj_cross = self.get_cross(self.kdj.KDJ_K, self.kdj.KDJ_D)

        self.rsi_over = self.get_over(self.rsi.RSI1, self.rsi.a, self.rsi.b, self.rsi.c, self.rsi.d, 50)
        self.kdj_over = self.get_over(self.kdj.KDJ_J, self.kdj.a, self.kdj.b, self.kdj.c, self.kdj.d, 55)
        self.cci_over = self.get_over(self.cci.CCI, self.cci.a, self.cci.b, self.cci.c, self.cci.d, 0)

        # ===================================================
        over_points = 0
        point = 0
        if self.kdj_over.over:
            point = self.kdj_over.degree if self.kdj_over.direction == AdviseX.Up else - self.kdj_over.degree \
                if self.kdj_over.direction == AdviseX.Down else 0
        over_points += point

        if self.rsi_over.over:
            point = self.rsi_over.degree if self.rsi_over.direction == AdviseX.Up else - self.rsi_over.degree \
                if self.rsi_over.direction == AdviseX.Down else 0
        over_points += point

        if self.cci_over.over:
            point = self.cci_over.degree if self.cci_over.direction == AdviseX.Up else - self.cci_over.degree \
                if self.cci_over.direction == AdviseX.Down else 0
        over_points += point
        Indicator_tips = f"{bar.running_time} 超线压力={over_points:.2f} "

        # ===================================================

        cross_points = 0
        tips = ""
        point, tip = self.get_cross_points(self.macd_cross, "macd")
        tips += tip
        cross_points += point
        point, tip = self.get_cross_points(self.rsi_cross, "rsi", self.rsi_over)
        tips += tip
        cross_points += point
        point, tip = self.get_cross_points(self.kdj_cross, "kdj", self.kdj_over)
        tips += tip
        cross_points += point
        if tips != "":
            Indicator_tips += tips

        turn_points = 0
        tips = ""
        point = 0
        if self.kdj_over.m_turn:
            point = 10 if self.kdj_over.direction == AdviseX.Up else -10 if self.kdj_over.direction == AdviseX.Down else 0
        elif self.kdj_over.turn:
            point = 1 if self.kdj_over.direction == AdviseX.Up else -1 if self.kdj_over.direction == AdviseX.Down else 0
        turn_points += point
        self.advise.kdj_turn_points = point
        tips += "kdj:m头 " if self.kdj_over.m_turn else ""
        tips += "kdj:turn " + f"{self.kdj_over.direction}({self.kdj_over.degree:.2f} : {self.kdj.KDJ_J[-2]:.2f})" if self.kdj_over.turn else ""

        point = 0
        if self.rsi_over.m_turn:
            point = 10 if self.rsi_over.direction == AdviseX.Up else -10 if self.rsi_over.direction == AdviseX.Down else 0
        elif self.rsi_over.turn:
            point = 1 if self.rsi_over.direction == AdviseX.Up else -1 if self.rsi_over.direction == AdviseX.Down else 0
        turn_points += point
        self.advise.rsi_turn_points = point
        tips += "rsi:m头 " if self.rsi_over.m_turn else ""
        tips += "rsi:turn " + f"{self.rsi_over.direction}({self.rsi_over.degree:.2f} : {self.rsi.RSI1[-2]:.2f})" if self.rsi_over.turn else ""

        point = 0
        if self.cci_over.m_turn:
            point = 10 if self.cci_over.direction == AdviseX.Up else -10 if self.cci_over.direction == AdviseX.Down else 0
        elif self.cci_over.turn:
            point = 1 if self.cci_over.direction == AdviseX.Up else -1 if self.cci_over.direction == AdviseX.Down else 0
        turn_points += point
        self.advise.cci_turn_points = point
        tips += "cci:m头 " if self.cci_over.m_turn else ""
        tips += "cci:turn " + f"{self.cci_over.direction}({self.cci_over.degree:.2f} : {self.cci.CCI[-2]:.2f})" if self.cci_over.turn else ""
        if tips != "":
            Indicator_tips += tips

        print(Indicator_tips)

        self.advise.over_points = over_points
        self.advise.cross_points = cross_points
        self.advise.turn_points = turn_points
        return self.advise

    def get_cross_points(self, cross, indicator, over=None):
        point = 1 if cross.cross == CrossX.Jx else -1 if cross.cross == CrossX.Sx else 0
        point = point * 3 if cross.twice else point
        tips = ""
        if cross.twice:
            tips += f"{indicator}:jx2 " if point > 0 else f"{indicator}:sx2 " if point < 0 else ""
        else:
            tips += f"{indicator}:jx" if point > 0 else f"{indicator}:sx " if point < 0 else ""

        tips += f"({over.degree:.2f}) " if over is not None and point > 0 else " "

        return point, tips

    def get_over(self, A, t_overbought, t_oversold, t_superbought, t_supersold, t_middle):
        if A[-1] > t_middle:
            signal = self.get_over_signal(A, Common.has_top_turn, t_overbought, t_superbought, t_middle)
        else:
            signal = self.get_over_signal(A, Common.has_bottom_turn, t_oversold, t_supersold, t_middle)
        return signal

    def get_over_signal(self, s, turn_func, t_over, t_super, t_middle):  # self.has_bottom_turn
        bar = self.bar
        A = s.copy()
        # TRUN 需要改，只要超过线，就是turn****************8
        is_high = (t_over > t_middle).all()  # top
        is_low = (t_over < t_middle).all()  # bottom
        signal = OverSignal()
        signal.degree = 0
        signal.direction = AdviseX.Unknown
        signal.value = A[-1]

        signal.turn = A.dropna().rolling(3).apply(turn_func).dropna()
        signal.turn = signal.turn[signal.turn != 0]
        turn_length = len(signal.turn)

        signal.m_turn = False
        turn_now = A[-1:].index.codes[0][0] == signal.turn.index.codes[0][-1] if turn_length > 0 else False
        signal.twice = pd.Series(signal.turn.index.codes[0]).diff().dropna().values[-1] < 5 if turn_length > 1 and turn_now else False
        signal.turn = True if turn_length > 0 and turn_now else False

        if is_low:
            A[A > t_over] = 0
            A[A < t_over] = 1 + (t_over - A[A < t_over]) / (t_over - t_super)
        elif is_high:
            A[A < t_over] = 0
            A[A > t_over] = 1 + (A[A > t_over] - t_over) / (t_super - t_over)

        over = A[A != 0]
        over_length = len(over)

        signal.twice = pd.Series(over.index.codes[0]).diff().dropna().values[0] < 5 if over_length > 1 else False

        if over_length > 0:
            signal.over = True
            last_over_datetime = over[-1:].index[0]
            signal.degree = A[last_over_datetime] * (2 if signal.twice else 1)
        else:
            signal.over = False

        if signal.m_turn or signal.twice or signal.turn or signal.over:
            signal.direction = AdviseX.Up if is_low else AdviseX.Down if is_high else AdviseX.Unknown

        return signal

    def get_cross(self, A, B, Trend_Line=None):
        signal = CrossSignal()
        # 过滤掉只穿过一点点的
        delta = 1.5  # 0.9
        jx = QA.CROSS(A - delta, B)
        sx = QA.CROSS(B - delta, A)
        res = jx - sx
        signal.cross = res[res != 0]
        cross_length = len(signal.cross)
        if cross_length > 0:
            cross_now = A[-1:].index.codes[0][0] == signal.cross.index.codes[0][-1]
        else:
            cross_now = False

        if cross_length > 1 and cross_now:
            x_value = signal.cross[-1]
            x_series = signal.cross[signal.cross == x_value]
            signal.twice = pd.Series(x_series.index.codes[0]).diff().dropna().values[-1] < 5 if len(x_series) > 1 else False
        else:
            signal.twice = False

        if cross_now:
            last_x_value = signal.cross[-1]
            signal.cross = CrossX.Jx if last_x_value == 1 else CrossX.Sx if last_x_value == -1 else CrossX.Unknown
        else:
            signal.cross = CrossX.Unknown

        return signal
